
#include "Win32.Diagnostics.Log.h"
#include "Win32.Drawing.AutoCad.DfxReader.h"
#include "Win32.Number.h"

// -----------------------------------------------------------------------------

namespace Kif
{
namespace Win32
{
namespace Drawing
{
namespace AutoCad
{

// -----------------------------------------------------------------------------
// Constructor...

DfxReader::DfxReader()
{
   int i;
   for (i=0; i < maxSections; i++)
      sections[i].Buffer = NULL;

   // define supported sections

   sections[0].ItemIndex   = 0;
   sections[0].Identifier  = keys.Header;
   sections[0].IdLength    = keys.HeaderLength;
   sections[0].funcHandler = &DfxReader::ParseHeader;

   sections[1].ItemIndex   = 1;
   sections[1].Identifier  = keys.Classes;
   sections[1].IdLength    = keys.ClassesLength;
   sections[1].funcHandler = &DfxReader::ParseClasses;

   sections[2].ItemIndex   = 2;
   sections[2].Identifier  = keys.Tables;
   sections[2].IdLength    = keys.TablesLength;
   sections[2].funcHandler = &DfxReader::ParseTables;

   sections[3].ItemIndex   = 3;
   sections[3].Identifier  = keys.Blocks;
   sections[3].IdLength    = keys.BlocksLength;
   sections[3].funcHandler = &DfxReader::ParseBlocks;

   sections[4].ItemIndex   = 4;
   sections[4].Identifier  = keys.Entities;
   sections[4].IdLength    = keys.EntitiesLength;
   sections[4].funcHandler = &DfxReader::ParseEntities;

   sections[5].ItemIndex   = 5;
   sections[5].Identifier  = keys.Objects;
   sections[5].IdLength    = keys.ObjectsLength;
   sections[5].funcHandler = &DfxReader::ParseObjects;

   sections[6].ItemIndex   = 6;
   sections[6].Identifier  = keys.ThumbNailImage;
   sections[6].IdLength    = keys.ThumbNailImageLength;
   sections[6].funcHandler = &DfxReader::ParseThumbNailImage;

   // define supported entities

   entities[0].ItemIndex   = 0;
   entities[0].Identifier  = "LINE";
   entities[0].IdLength    = 4;
   entities[0].funcHandler = &DfxReader::ParseEntityLine;

   entities[1].ItemIndex   = 1;
   entities[1].Identifier  = "LWPOLYLINE";
   entities[1].IdLength    = 10;
   entities[1].funcHandler = &DfxReader::ParseEntityPolyLine;

   Verbosity = Kif::Win32::Diagnostics::Verbose_None;

   vectorSet.Alloc(100000);
}  // end of DfxReader (ctor)

// -----------------------------------------------------------------------------
// Function Handlers

/// <summary>Parse Section</summary>
/// <returns>buffer pointer to following input chars is returned</returns>
char *DfxReader::ParseHeader(char *buff)
{
   return(buff);
}  // end of ParseHeader

/// <summary>Parse Section</summary>
/// <returns>buffer pointer to following input chars is returned</returns>
char *DfxReader::ParseClasses(char *buff)
{
   return(buff);
}  // end of ParseClasses

/// <summary>Parse Section</summary>
/// <returns>buffer pointer to following input chars is returned</returns>
char *DfxReader::ParseTables(char *buff)
{
   return(buff);
}  // end of ParseTables

/// <summary>Parse Section</summary>
/// <returns>buffer pointer to following input chars is returned</returns>
char *DfxReader::ParseBlocks(char *buff)
{
   return(buff);
}  // end of ParseBlocks

/// <summary>Parse Entity Handle</summary>
/// <returns>buffer pointer to following input chars is returned</returns>
char *DfxReader::ParseEntityHandle(char *buff)
{
   // handle should follow '  5'
   if (Keywords::Compare("  5",buff))
      buff += 5; // '  5' cr lf (5 chars)
   else
      return NULL;

   // for now skip handle
   buff = TxtParse::Parse::SkipToken(buff);
}  // end of ParseEntityHandle

/// <summary>Parse Entity Type</summary>
/// <returns>buffer pointer to following input chars is returned</returns>
char *DfxReader::ParseEntityType(char *buff,char *expectedBuffer)
{
   // subclass marker should follow '100' + 'entity-type'
   if (Keywords::Compare("100",buff))
   {
      buff += 5; // '100' cr lf (5 chars)
      // we should find 'AcDbLine' now
      if (Keywords::Compare(expectedBuffer,buff))
         buff = TxtParse::Parse::SkipToken(buff);
      else
         return NULL;
   }
   else
      return NULL;
   return(buff);
}  // end of ParseEntityType

/// <summary>Parse Entity Layer Name</summary>
/// <returns>buffer pointer to following input chars is returned</returns>
char *DfxReader::ParseEntityLayerName(char *buff)
{
   char layerName[maxNameLength+1];
   if (Keywords::Compare("  8",buff))
   {
      buff += 5; // '100' cr lf (5 chars)
      // we should find the layer name
      try
      {
         buff = TxtParse::Parse::GetToken(layerName,maxNameLength,buff);
      }
      catch (TCHAR *ex)
      {
         Kif::Win32::Diagnostics::Log::WriteMessage(
            ex,Kif::Win32::Diagnostics::LogTo_ErrorLogOnly);
         buff = NULL;
      }
      if (buff == NULL)
         return NULL;

      // are we managing the current item or found a new one?
      if (Kif::Win32::AnsiString::Compare(currentItemName,layerName) != 0)
      {
         // store vector
         if (vectorSet.Count > 0)
            vectorSet.FileWrite(currentItemName);
         vectorSet.Count = 0;

         // prepare for new vector
         Kif::Win32::AnsiString::Copy(currentItemName,maxNameLength,layerName);

         // provide feedback to user
         if (Verbosity == Kif::Win32::Diagnostics::Verbose_Trace)
         {
            Kif::Win32::String str;
            str.Copy(layerName);
            Kif::Win32::Diagnostics::Log::WriteMessage(str.GetBuffer());
         }
      }
   }
   else
      return NULL;
   return(buff);
}  // end of ParseEntityLayerName

/// <summary>Parse Entity Get Number of Vertices</summary>
/// <param name="buff">input buffer</param>
/// <param name="numVertices">output number of vertices</param>
/// <returns>buffer pointer to following input chars is returned</returns>
char *DfxReader::ParseEntityGetNumberOfVertices(char *buff, long *numVertices)
{
   // subclass marker should follow ' 90' + 'number of vertices'
   if (Keywords::Compare(" 90",buff))
   {
      buff += 5; // ' 90' cr lf (5 chars)
      // we should find 'the number of vetices' now
      char numv[maxNameLength+1];
      buff = TxtParse::Parse::GetToken(numv,maxNameLength,buff);
      Kif::Win32::String str;
      str.Copy(numv);
      *numVertices = Kif::Win32::Number::ToLong(str.GetBuffer());
   }
   else
      return NULL;
   return(buff);
}  // end of ParseEntityType

/// <summary>Parse Entity Is PolyLine Closed</summary>
/// <param name="buff">input buffer</param>
/// <param name="isClosed">output true if closed</param>
/// <returns>buffer pointer to following input chars is returned</returns>
char *DfxReader::ParseEntityIsPolyLineClose(char *buff, bool *isClosed)
{
   *isClosed = false;
   // subclass marker should follow ' 70' + '1 (for closed)'
   if (Keywords::Compare(" 70",buff))
   {
      buff += 5; // ' 70' cr lf (5 chars)
      // we should find 'the number of vetices' now
      char tk[maxNameLength+1];
      buff = TxtParse::Parse::GetToken(tk,maxNameLength,buff);
      *isClosed = ((tk[0] == '1') && (tk[1] == NULL));
   }
   else
      return NULL;
   return(buff);
}  // end of ParseEntityIsPolyLineClose

/// <summary>Parse Entity optional line type</summary>
/// <param name="buff">input buffer</param>
/// <param name="pLineType">output the line type/param>
/// <returns>buffer pointer to following input chars is returned</returns>
char *DfxReader::ParseEntityLineType(char *buff, int *pLineType)
{
   *pLineType = -1;
   // subclass marker should follow ' 70' + '1 (for closed)'
   if (Keywords::Compare("  6",buff))
   {
      buff += 5; // ' 70' cr lf (5 chars)
      // we should find 'the number of vetices' now
      char tk[maxNameLength+1];
      buff = TxtParse::Parse::GetToken(tk,maxNameLength,buff);
      if (Kif::Win32::AnsiString::Compare(tk,"CONTINUOUS") == 0)
         *pLineType = 0;
   }
   return(buff);
}  // end of ParseEntityLineType

/// <summary>Parse Entity</summary>
/// <returns>buffer pointer to following input chars is returned</returns>
char *DfxReader::ParseEntityLine(char *buff)
{
   // handle should follow '  5'
   buff = ParseEntityHandle(buff);

   // subclass marker should follow '100' + 'AcDbEntity'
   buff = ParseEntityType(buff,keys.AcDbEntity);

   // subclass marker should follow '  8' + '<Layer-Name>'
   buff = ParseEntityLayerName(buff);

   // subclass marker should follow '100' + 'AcDbLine'
   buff = ParseEntityType(buff,"AcDbLine");

   // now we should find start-line (10 x 20 y 30 z)

   // subclass marker should follow '100' + 'AcDbLine'
   char x1[maxNumericLength+1];
   char y1[maxNumericLength+1];
   char z1[maxNumericLength+1];

   buff = GetItem(" 10",3,x1,maxNumericLength,buff);  // start x
   buff = GetItem(" 20",3,y1,maxNumericLength,buff);  // start y
   buff = GetItem(" 30",3,z1,maxNumericLength,buff);  // start z

   // now we should find end-line (11 x 21 y 31 z)
   char x2[maxNumericLength+1];
   char y2[maxNumericLength+1];
   char z2[maxNumericLength+1];

   buff = GetItem(" 11",3,x2,maxNumericLength,buff);  // end x
   buff = GetItem(" 21",3,y2,maxNumericLength,buff);  // end y
   buff = GetItem(" 31",3,z2,maxNumericLength,buff);  // end z

   // add line into vectors
   double px1,py1,pz1,px2,py2,pz2;
   px1 = Kif::Win32::Number::ToDouble(x1);
   py1 = Kif::Win32::Number::ToDouble(y1);
   pz1 = Kif::Win32::Number::ToDouble(z1);

   px2 = Kif::Win32::Number::ToDouble(x2);
   py2 = Kif::Win32::Number::ToDouble(y2);
   pz2 = Kif::Win32::Number::ToDouble(z2);

   vectorSet.Add(px1,py1,pz1,px2,py2,pz2);

   // provide feedback to user
   if (Verbosity == Kif::Win32::Diagnostics::Verbose_Trace)
   {
      Kif::Win32::String str;
      str.Copy(L"l",1);
      str.Concat(x1,Kif::Win32::String::getLength(x1));
      str.Concat(L" ");
      str.Concat(y1,Kif::Win32::String::getLength(y1));
      str.Concat(L" ");
      str.Concat(z1,Kif::Win32::String::getLength(z1));
      str.Concat(L" ");
      str.Concat(x2,Kif::Win32::String::getLength(x2));
      str.Concat(L" ");
      str.Concat(y2,Kif::Win32::String::getLength(y2));
      str.Concat(L" ");
      str.Concat(z2,Kif::Win32::String::getLength(z2));
      str.Concat(L" ");

      Kif::Win32::Diagnostics::Log::WriteMessage(str.GetBuffer());
   }

   return(buff);
}  // end of ParseEntityLine

/// <summary>Parse Entity</summary>
/// <returns>buffer pointer to following input chars is returned</returns>
char *DfxReader::ParseEntityPolyLine(char *buff)
{
   // handle should follow '  5'
   buff = ParseEntityHandle(buff);

   // subclass marker should follow '100' + 'AcDbEntity'
   buff = ParseEntityType(buff,keys.AcDbEntity);

   // subclass marker should follow '  8' + '<Layer-Name>'
   buff = ParseEntityLayerName(buff);

   // optional line type
   int lineType;
   buff = ParseEntityLineType(buff,&lineType);

   // subclass marker should follow '100' + 'AcDbPolyline'
   buff = ParseEntityType(buff,"AcDbPolyline");

   // get number of vertices
   long numVerts;
   buff = ParseEntityGetNumberOfVertices(buff, &numVerts);

   // get polygon close (=1) or plinegen (=128)
   bool isClosed;
   buff = ParseEntityIsPolyLineClose(buff, &isClosed);

   // read all vertices...
   int i;
   char x1[maxNumericLength+1];
   char y1[maxNumericLength+1];

   for(i=0; i < numVerts; i++)
   {
      buff = GetItem(" 10",3,x1,maxNumericLength,buff);
      buff = GetItem(" 20",3,y1,maxNumericLength,buff);

      // add line into vectors
      double px1,py1,pz1 = 0.0;
      px1 = Kif::Win32::Number::ToDouble(x1);
      py1 = Kif::Win32::Number::ToDouble(y1);

      vectorSet.Add(px1,py1,pz1);

      // provide feedback to user
      if (Verbosity == Kif::Win32::Diagnostics::Verbose_Trace)
      {
         Kif::Win32::String str;
         str.Copy(L"l",1);
         str.Concat(x1,Kif::Win32::String::getLength(x1));
         str.Concat(L" ");
         str.Concat(y1,Kif::Win32::String::getLength(y1));

         Kif::Win32::Diagnostics::Log::WriteMessage(str.GetBuffer());
      }
   }

   return(buff);
}  // end of ParseEntityPolyLine

/// <summary>Parse Section</summary>
/// <returns>buffer pointer to following input chars is returned</returns>
char *DfxReader::ParseEntities(char *buff)
{
   bool found;

   // skip section name...
   buff += keys.EntitiesLength + 2; // ENTITIES cr lf
   *currentItemName = NULL;         // prepare for new item...

   // scan for all defined entities
   int i;

   while (true)
   {
      // must find 0 cr lf ( 0 = entity-type)
      if (Keywords::Compare("  0",buff))
         buff += 5; // '  0' cr lf (5 chars)
      else
         return NULL;

      // are we finished yet? (found the ENDSECTION?)
      if (Keywords::Compare(keys.EndSection,buff))
      {
         buff += keys.EndSectionLength + 2; // 'ENDSEC' cr lf
         return(buff);
      }

      // try to find entity base on it's type-name (ex. LINE)
      found = false;
      for (i=0; i < maxEntities; i++)
      {
         if (Keywords::Compare(entities[i].Identifier,buff))
         {
            buff += entities[i].IdLength + 2; // <entity-id> cr lf
            buff = (this->*entities[i].funcHandler)(buff);
            found = true;
            break;
         }
      }

      // if not found provide some feedback to user...
      if (!found)
      {
         char name[maxNameLength+1];
         String str;
         TxtParse::Parse::GetToken(name,maxNameLength,buff);
         str.Copy(L"Unrecognized Entity(");
         str.Concat(name,Kif::Win32::String::getLength(name));
         str.Concat(L")");
         Kif::Win32::Diagnostics::Log::WriteMessage(str.GetBuffer());
         break;
      }
   }

   return(buff);
}  // end of ParseEntities

/// <summary>Parse Section</summary>
/// <returns>buffer pointer to following input chars is returned</returns>
char *DfxReader::ParseObjects(char *buff)
{
   return(buff);
}  // end of ParseObjects

/// <summary>Parse Section</summary>
/// <returns>buffer pointer to following input chars is returned</returns>
char *DfxReader::ParseThumbNailImage(char *buff)
{
   return(buff);
}  // end of ParseThumbNailImage

// -----------------------------------------------------------------------------

/// <summary>Open Dfx file and read it into RAM buffer</summary>
/// <param name="filePath">file path name</param>
bool DfxReader::Open(TCHAR *filePath)
{
   // line delimited = cr + lf (13 + 10)
   bool done = dfxBuffer.Open(filePath);
   return(done);
}  // end of Open

/// <summary>Close Dfx buffer</summary>
void DfxReader::Close()
{
   CloseOutput();
   dfxBuffer.Close();
}  // end of Close

// -----------------------------------------------------------------------------

/// <summary>Scan the buffer until next section</summary>
/// <param name="buff">char buffer to search next section</param>
/// <returns>A pointer to the the found section</returns>
DfxReader::Item *DfxReader::GetNextSection(char *buff)
{
   // loop through all the buffer content until the next section is found
   char *bf;
   while (*buff != NULL)
   {
      buff = TxtParse::Parse::SkipTralingBlanks(buff);

      // find next SECTION
      bf = IsSection(buff);
      if (bf != NULL)
      {
         Item *pSec = NULL;
         int i;
         for (i=0; i<maxSections; i++)
         {
            // if section.Buffer is not visited then scan it...
            // else section was already visited...
            if (sections[i].Buffer == NULL)
            {
               if (Keywords::Compare(sections[i].Identifier,bf))
               {
                  pSec = sections + i;
                  sections[i].Buffer = bf;

                  // provide some feedback to user if requested...
                  if (Verbosity == Kif::Win32::Diagnostics::Verbose_Trace)
                  {
                     Kif::Win32::String str;
                     str.Copy(L"SECTION (");
                     str.Concat(sections[i].Identifier,
                                sections[i].IdLength);
                     str.Concat(L")");
                     Kif::Win32::Diagnostics::Log::WriteMessage(
                        str.GetBuffer());
                  }

                  break;
               }
            }
         }
         return(pSec);
      }
      else
      {
         buff++;
         continue;
      }
   }
   return(NULL);
}  // end of GetNextSection

/// <summary>Parse file content</summary>
void DfxReader::Parse()
{
   // loop through all the file content, (through all sections)
   Item *pSec;
   ParseHandler parse;
   char *buff = dfxBuffer.GetBuffer(), *bf;
   while (*buff != NULL)
   {
      pSec = GetNextSection(buff);
      if (pSec == NULL)
         break;

      // parse section
      buff = (this->*pSec->funcHandler)(pSec->Buffer);
   }
}  // end of Parse

}  // end of Kif.Drawing.AutoCad
}  // end of Kif.Drawing
}  // end of Kif.Win32
}  // end of Kif namespace

